package matchbox;

import java.text.MessageFormat;
import java.util.ArrayList;

import org.omg.CORBA.IMP_LIMIT;

import basics.DenseMatrix;
import basics.DenseVector;
import basics.VectorMatrix;
import probability.GaussianDistribution;
import probability.Indicator.TruncatedGaussianType;
import probability.Indicator;
import probability.ProbabilityDistribution;
import probability.UniformDistribution;
import utils.Printer;
import inference.GraphFactor;
import inference.GraphNode;
import inference.BeliefMessage;
import inference.GraphUtils;

public class RRFactor extends MatchboxFactor {

	private DenseMatrix _beta = new DenseMatrix(new double[][] { { 0.01 } });
	private Double _observedRate = null;
	private GaussianDistribution _tor;

	public RRFactor(String vname) {
		super(vname, Type.Operation);
	}

	@Override
	public BeliefMessage toCached(GraphNode next) {
		return toCached(next, null);
	}

	@Override
	public BeliefMessage toCached(GraphNode next, String opt) {
		BeliefMessage m = null;
		if (next.is(NodeNames.RT)) {
			m = new BeliefMessage(new GaussianDistribution(_observedRate, 0.01));
		} else {
			m = new BeliefMessage(_tor);
		}

		return m;
	}

	public BeliefMessage to(GraphNode next, String opt) {
		BeliefMessage m = null;
		if (next.is(NodeNames.RT)) {
			// this is the EP approximation
			// double r = m.getDistribution().measure(new DenseVector(new double[] { _observedRate }));
			// m.getDistribution().setParam(0, new DenseVector(new double[] { _observedRate }));
			m = new BeliefMessage(observedProb());

			// final double epsilon = 0;
			// if (_observedRate > epsilon && r < epsilon) {
			// double mu = m.getDistribution().getParam(0).get(0);
			// double sigma = m.getDistribution().getParam(1).get(0, 0);
			//
			// double pi = Math.pow(sigma, -2);
			// double taw = pi * mu;
			//
			// // double recMu = mu + sigma * Indicator.computeV(mu / sigma, l / sigma);
			// // double recSig = sigma * sigma * (1 - computeW(mu / sigma, l / sigma));
			// double newPi = Indicator.computePi(-pi, -taw, epsilon, TruncatedGaussianType.xGreaterE);
			// double newTaw = Indicator.computeTaw(-pi, -taw, epsilon, TruncatedGaussianType.xGreaterE);
			// sigma = Math.pow(newPi, -.5);
			// mu = newTaw / newPi;
			// m.getDistribution().getParam(0).set(mu, 0);
			// m.getDistribution().getParam(1).set(taw, 0, 0);
			// }
			// m.setValue(new DenseMatrix(new double[][] { { r } }));
		} else {
			m = toR(opt);
			_tor = (GaussianDistribution) m.getDistribution();

			Printer.appendToFile("r_dist.csv", m.getDistribution().toStringCSV());
		}

		Printer.print("F_RR>" + next + ":" + m.getDistribution().toString());

		return m;
	}

	private GaussianDistribution observedProb() {
		return new GaussianDistribution(_observedRate, 0.01);
	}

	@Override
	public ProbabilityDistribution marginal() {
		return GaussianDistribution.product(observedProb(), _tor);
	}

	@Override
	public GaussianDistribution ep(GraphNode next) {
		GaussianDistribution g = null;
		if (!next.is(NodeNames.RT)) {
			GaussianDistribution m = (GaussianDistribution) marginal();
			g = m.divide(observedProb());
			_tor = g;
		}

		return _tor;
	}

	private ArrayList<STFactor> _sFactor = new ArrayList<STFactor>();
	private ArrayList<STFactor> _tFactor = new ArrayList<STFactor>();

	private DenseVector _U; // DenseMatrix
	private DenseVector _V;
	private int _count;
	private double _imp_mean;

	public void addST(STFactor fs, STFactor ft) {
		_sFactor.add(fs);
		_tFactor.add(ft);
	}

	public void setObservedRate(double r) {
		_observedRate = r;
		// double r = ((GaussianDistribution) m.getDistribution()).getMu();
		_count++;
		_imp_mean = _imp_mean + (r - _imp_mean) / (double) (_count);

		_U = updateUVMatrix(_U, _sFactor);
		_V = updateUVMatrix(_V, _tFactor);

		// mean used to be calcualted here :D
	}

	private BeliefMessage toR(String opt) {
		BeliefMessage m = edges().get(NodeNames.RT).nodes().get(0).to(this, opt);
		GaussianDistribution g = (GaussianDistribution) m.getDistribution();
		g.setMu(g.getMu()+ _imp_mean); //  + getBias() 

		if (_max != null && g.getMu() > _max) {
			g.setMu(_max);
		} else if (_min != null && g.getMu() < _min) {
			g.setMu(_min);
		}
		// ProbabilityDistribution newp = new GaussianDistribution(m.getDistribution().getParamsCopy());
		// newp.getParam(1).add(_beta); // new BeliefMessage(newp);
		return m;
	}

	private DenseVector updateUVMatrix(DenseVector uv, ArrayList<STFactor> fst) {
		// uv = fst.get(0).getXMean();
		if (uv == null) {
			uv = new DenseVector(fst.get(0).getX().length(), 0);
		}

		// for (int k = 0; k < fst.size(); k++) {

		for (int i = 0; i < fst.get(0).getX().length(); i++) {
			uv.set(uv.get(i) + (fst.get(0).getX().get(i) - uv.get(i)) / (double) _count, i);
		}
		// }

		return uv;
	}

	private double getBias() {
		if (_U == null || _V == null)
			return 0;

		// double xu = _sFactor.get(0).getX().times(_U.transpose()).sumAllElements();
		// double yv = _tFactor.get(0).getX().times(_V.transpose()).sumAllElements();

		double xu = _sFactor.get(0).getX().times(_U).sumAllElements();
		double yv = _tFactor.get(0).getX().times(_V).sumAllElements();

		return xu + yv;
	}

	private Double _min = null, _max = null;

	public void setBounds(double min, double max) {
		_min = min;
		_max = max;
	}

}
